﻿using Common.Logging.Factory;
using Gibraltar.Agent;
using System;

namespace Common.Logging.Loupe
{
    /// <summary>
    /// This class provides a proxy between Common.Logging and Loupe Log.
    /// </summary>
    public class LoupeLogger: AbstractLogger
    {
        #region Fields

        // Log system description.
        private const string LOG_SYSTEM = "Common.Logging";

        // The number of frames to skip when writing entries.
        private const int SKIP_FRAMES = 2;

        #endregion

        #region Constructor / Destructor

        /// <summary>
        /// Creates a new instance of type LoupeLogger
        /// </summary>
        /// <param name="name">The name of the logger.</param>
        /// <param name="currentLevel">The currently active level.</param>
        public LoupeLogger(string name, LogLevel currentLevel)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentException("Name may not be null or empty");
            }

            Name = name;
            CurrentLevel = currentLevel;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the CurrentLevel
        /// </summary>
        public LogLevel CurrentLevel { get; set; }

        /// <summary>
        /// Gets or sets the name
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Gets or sets a flag indicating whether the Debug level is enabled
        /// </summary>
        public override bool IsDebugEnabled
        {
            get { return LevelIsEnabled(LogLevel.Debug); }
        }

        /// <summary>
        /// Gets or sets a flag indicating whether the Error level is enabled
        /// </summary>
        public override bool IsErrorEnabled
        {
            get { return LevelIsEnabled(LogLevel.Error); }
        }

        /// <summary>
        /// Gets or sets a flag indicating whether the Fatal level is enabled
        /// </summary>
        public override bool IsFatalEnabled
        {
            get { return LevelIsEnabled(LogLevel.Fatal); }
        }

        /// <summary>
        /// Gets or sets a flag indicating whether the Info level is enabled
        /// </summary>
        public override bool IsInfoEnabled
        {
            get { return LevelIsEnabled(LogLevel.Info); }
        }

        /// <summary>
        /// Gets or sets a flag indicating whether the Trace level is enabled
        /// </summary>
        public override bool IsTraceEnabled
        {
            get { return LevelIsEnabled(LogLevel.Trace); }
        }

        /// <summary>
        /// Gets or sets a flag indicating whether the Warn level is enabled
        /// </summary>
        public override bool IsWarnEnabled
        {
            get { return LevelIsEnabled(LogLevel.Warn); }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Writes the provided message to the underlying logging system
        /// </summary>
        /// <param name="level">Requested level.</param>
        /// <param name="message">The message that should be logged.</param>
        /// <param name="exception">An exception message that should be logged.</param>
        protected override void WriteInternal(LogLevel level, object message, Exception exception)
        {
            switch (level)
            {
                case LogLevel.Off:
                    // Do nothing
                    break;

                case LogLevel.All:
                    // Always log
                    Log.Write(LogMessageSeverity.Verbose, LOG_SYSTEM, SKIP_FRAMES, exception, LogWriteMode.Queued, null, Name, message != null ? message.ToString() : null, null, null);
                    break;


                case LogLevel.Trace:
                    if (IsTraceEnabled)
                    {
                        Log.Write(LogMessageSeverity.Verbose, LOG_SYSTEM, SKIP_FRAMES, exception, LogWriteMode.Queued, null, Name, message != null ? message.ToString() : null, null, null);
                    }
                    break;

                case LogLevel.Debug:
                    if (IsDebugEnabled)
                    {
                        Log.Write(LogMessageSeverity.Verbose, LOG_SYSTEM, SKIP_FRAMES, exception, LogWriteMode.Queued, null, Name, message != null ? message.ToString() : null, null, null);
                    }
                    break;
                
                case LogLevel.Error:
                    if (IsErrorEnabled)
                    {
                        Log.Write(LogMessageSeverity.Error, LOG_SYSTEM, SKIP_FRAMES, exception, LogWriteMode.Queued, null, Name, message != null ? message.ToString() : null, null, null);
                    }
                    break;
                
                case LogLevel.Fatal:
                    if (IsFatalEnabled)
                    {
                        // We are switching to a "WaitForCommit" for a critical error to give us the best chance of actually logging this critical error
                        Log.Write(LogMessageSeverity.Critical, LOG_SYSTEM, SKIP_FRAMES, exception, LogWriteMode.WaitForCommit, null, Name, message != null ? message.ToString() : null, null, null);
                    }
                    break;
                
                case LogLevel.Info:
                    if (IsInfoEnabled)
                    {
                        Log.Write(LogMessageSeverity.Information, LOG_SYSTEM, SKIP_FRAMES, exception, LogWriteMode.Queued, null, Name, message != null ? message.ToString() : null, null, null);
                    } 
                
                    break;
                
                case LogLevel.Warn:
                    if (IsWarnEnabled)
                    {
                        Log.Write(LogMessageSeverity.Warning, LOG_SYSTEM, SKIP_FRAMES, exception, LogWriteMode.Queued, null, Name, message != null ? message.ToString() : null, null, null);
                    }
                    break;
            }
        }        

        /// <summary>
        /// Determines whether the CurrentLevel is high enough that the 
        /// provided LogLevel is enabled.
        /// </summary>
        /// <param name="level">Requested level.</param>
        /// <returns>true if the level is enabled; false otherwise.</returns>
        private bool LevelIsEnabled(LogLevel level)
        {
            int iLevel = (int)level;
            int iCurrentLogLevel = (int)CurrentLevel;

            // return iLevel.CompareTo(iCurrentLogLevel); better ???
            return (iLevel >= iCurrentLogLevel);
        }

        #endregion        
    }
}
